/**********************************

	*********************************
				SF2D Engine
	*********************************

	A Class that Defines Allows 
	Dynamic Binding of keys to
	Actions.

	By Julian Williams
	Jan 22, 2010
***********************************/


#include <iostream>
#include <assert.h>
using namespace std;
//#include <SFML/Graphics.hpp>

#include "input.h"
#include "app.h"


Dynamic_Input Binder;

//Static property
sf::Event Dynamic_Input::input_event;


//Proxy functions for script engine
void Input_assign_key_button( Dynamic_Input * input, const string& str, const bool autoRepeat, int k)
{
	sf::Key::Code kCode = static_cast< sf::Key::Code > (k);

	input->Assign_Button(str, autoRepeat, kCode);
}

void Input_assign_mouse_button( Dynamic_Input * input, const string& str, const bool autoRepeat, int b)
{
	sf::Mouse::Button mCode = static_cast< sf::Mouse::Button > (b);

	input->Assign_Button(str, autoRepeat, mCode);
}


bool Input_test_action( Dynamic_Input * input, string& str )
{
	bool result =  input->Test_Action( str );
	return result;
}



bool Dynamic_Input::Register_With_ScriptEngine(string script_dir)
{

	int nRet = 0;

	

//Register the actual class
	
	nRet = aScriptEngine.GetEngine()->RegisterObjectType("Dynamic_Input", 0, asOBJ_REF | asOBJ_NOHANDLE); 
	assert( nRet >= 0 );
	
	nRet = aScriptEngine.GetEngine()->RegisterObjectMethod("Dynamic_Input", "bool test_action(string& )", asFUNCTION(Input_test_action), asCALL_CDECL_OBJFIRST);
	assert( nRet >= 0 );
	
	nRet = aScriptEngine.GetEngine()->RegisterObjectMethod("Dynamic_Input", "void assign_key(string&, bool, int)", asFUNCTION(Input_assign_key_button), asCALL_CDECL_OBJFIRST);
	assert( nRet >= 0 );
	
	nRet = aScriptEngine.GetEngine()->RegisterObjectMethod("Dynamic_Input", "void assign_mbutton(string& , bool, int)", asFUNCTION(Input_assign_mouse_button), asCALL_CDECL_OBJFIRST);
	assert( nRet >= 0 );

	nRet = aScriptEngine.GetEngine()->RegisterGlobalProperty("Dynamic_Input Input", &Binder); 
	assert( nRet >= 0 );
	
	
	
// Angelscript class registration
	string script_location = script_dir + "input.as";
	cout << script_location << endl;

	//Add Script file to the Module
	if (aScriptEngine.AddSectionFromFile(script_location.c_str()) < 0)
	{
		cout << "Failed to load script file from: " << script_location.c_str() << endl;
	
		return false;
	}
	

	if(nRet < 0)
	{
		cout << "Register Class Result: " << nRet << endl;
		return false;
	}
	return true;
}//EoF





//Inject events for Button Events
void Dynamic_Input::Inject_Event(sf::Event &Event)
{
	input_event = Event;
}


//Load input script
bool Dynamic_Input::Load_Input(asIScriptContext * Context, string script_dir)
{


	//Load the script function
		int ID = aScriptEngine.GetModule()->GetFunctionIdByName("Input_Binds");
		cout << ID << endl;

		if(ID >= 0)
		{
			Context->Prepare( ID );

		}else{
			cout << "Invalid ID #: " << ID << endl;
			return false;
		}

			//Context->SetArgObject(0, &p);

		int r = Context->Execute();

		if( r != asEXECUTION_FINISHED )
		{
			// The execution didn't finish as we had planned. Determine why.
			if( r == asEXECUTION_ABORTED )
			{
				cout << "The script was aborted before it could finish. Probably it timed out." << endl;
				return false;
			}
			else if( r == asEXECUTION_EXCEPTION )
			{
				cout << "The script ended with an exception." << endl;

				// Write some information about the script exception
				int funcID = Context->GetExceptionFunction();
				asIScriptFunction *func = aScriptEngine.GetEngine()->GetFunctionDescriptorById(funcID);
				cout << "func: " << func->GetDeclaration() << endl;
				cout << "modl: " << func->GetModuleName() << endl;
				cout << "sect: " << func->GetScriptSectionName() << endl;
				cout << "line: " << Context->GetExceptionLineNumber() << endl;
				cout << "desc: " << Context->GetExceptionString() << endl;
				return false;
			}
			else
			{
				cout << "The script ended for some unforeseen reason (" << r << ")." << endl;
				return false;
			}
	  }

	  return true;
}//eof




//Assign a button to a specific function
//Neat little function, will allow for dynamic
//Binding of different keys to any action available
void Dynamic_Input::Assign_Button(std::string a, bool autoRepeat,  sf::Key::Code c)
{
	key.useAutoRepeat = autoRepeat;
	key.myKeyCode = c;

	//null the mouse code
	key.myMouseButton = sf::Mouse::ButtonCount;

    Keys[a] = key;
}//EoF



//for mouse
void Dynamic_Input::Assign_Button (std::string a, bool autoRepeat, sf::Mouse::Button b)
{
	key.useAutoRepeat = autoRepeat;
	key.myMouseButton = b;

	//null the key code
	key.myKeyCode = sf::Key::Count;

    Keys[a] = key;
}//EoF


bool Dynamic_Input::Test_Action (std::string s)
{
	// Auto Repeat used
	if(Keys[s].useAutoRepeat)
	{
		//check to see if key code is invalid ie, equil to count, if so, check mouse action,
		//othersise do the same for key action
		if(Keys[s].myKeyCode == sf::Key::Count)
		{
			if(App::getInstance().Get_RenderWindow()->GetInput().IsMouseButtonDown(Keys[s].myMouseButton))
			{
				//cout << "Condition 1 \n";
				return true;
			}

			//cout << "Condition 1 \n";

		}
		else if(Keys[s].myMouseButton == sf::Mouse::ButtonCount)
		{

			if(App::getInstance().Get_RenderWindow()->GetInput().IsKeyDown(Keys[s].myKeyCode))
			{
				//cout << "Condition 2 \n";
				return true;
			}
			//cout << "Condition 1 \n";

		}

//Else, not autorepeating
	}else{

		if(Keys[s].myKeyCode == sf::Key::Count)
		{
			if((input_event.Type == sf::Event::MouseButtonPressed) && (input_event.MouseButton.Button == Keys[s].myMouseButton))
			{


				input_event.Type = sf::Event::Count;
				return true;
			}
		}
		else if(Keys[s].myMouseButton == sf::Mouse::ButtonCount)
		{
			if((input_event.Type == sf::Event::KeyPressed) && (input_event.Key.Code == Keys[s].myKeyCode))
			{
				
				cout << "Condition 2 \n";
				input_event.Type = sf::Event::Count;
				return true;
			}
		}
	}//end ov autorepeat or not

    return (false);
}//EoF
